using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using Phoenix.Framework;

namespace Phoenix.Frames
{
    public class ContentFrameController : IContentFrameController
    {
        readonly Dictionary<NavigationRequest, List<IContentFrame>> children = new Dictionary<NavigationRequest, List<IContentFrame>>();
        readonly ChildFrameCollection childFrameCollection;
        readonly IContentFrameControl contentFrame;
        InvokeActionRequest currentForegroundOperation;
        bool isDeactivating;
        bool isActivating;
        bool isDisposing;
        bool isInvalidatingChildren;
        bool isClosing;

        public ContentFrameController(IContentFrameControl contentFrame)
        {
            this.contentFrame = contentFrame;
            childFrameCollection = new ChildFrameCollection(this);
        }

        public string Name
        {
            get { return contentFrame.Name; }
        }

        public NavigationRequest CurrentRequest { get; private set; }

        public IEnumerable<IContentFrame> Children
        {
            get
            {
                return CurrentRequest == null || !children.ContainsKey(CurrentRequest) ? Enumerable.Empty<IContentFrame>() : children[CurrentRequest];
            }
        }

        protected virtual void OnNavigatingTo(NavigationRequest navigationRequest)
        {
            DeactivateCurrentRequest();
        }

        public void NavigateTo(NavigationRequest navigationRequest)
        {
            var eventArgs = new RequestClosingEventArgs();
            CurrentRequestClosing(eventArgs);
            if (eventArgs.Cancel) return;

            OnNavigatingTo(navigationRequest);

            if (navigationRequest.View == null)
            {
                //TODO Possible create previous context?
                var invokeActionRequest = new InvokeActionRequest(null, navigationRequest.ControllerAction, navigationRequest.Parameters, navigationRequest.Options, this, navigationRequest.RequestTransientStore);
                Host.ActionInvoker.InvokeAction(invokeActionRequest);
                CurrentRequest = null;
            }
            else
            {
                CurrentRequest = navigationRequest;
                contentFrame.Content = navigationRequest.View;
                Activate(navigationRequest);
                OnNavigationComplete(new NavigationCompleteEventArgs(navigationRequest));
            }

            OnNavigatedTo(navigationRequest);
        }

        protected virtual void OnNavigatedTo(NavigationRequest navigationRequest)
        {
                
        }

        internal void Activate(NavigationRequest navigationRequest)
        {
            var viewModel = navigationRequest.View.DataContext as ViewModelBase;
            if (viewModel != null)
                viewModel.Activated(this, EventArgs.Empty);
        }

        public void BeginOperation(InvokeActionRequest invokeRequest)
        {
            if (IsForeground(invokeRequest.Options))
            {
                if (currentForegroundOperation != null)
                    throw new InvalidOperationException("Only one forground operation may be active at one time");

                OnForegroundOperationStarted(EventArgs.Empty);

                currentForegroundOperation = invokeRequest;

                if (ShowProgress(invokeRequest.Options))
                {
                    contentFrame.IsContentEnabled = false;
                    contentFrame.ContentOpacity = 0.2;
                    contentFrame.ShowProgressBar = true;
                    contentFrame.ProgressBarVisibility = Visibility.Visible;
                }
            }
        }

        public void EndOperation(InvokeActionRequest invokeRequest)
        {
            if (IsForeground(invokeRequest.Options))
            {
                if (currentForegroundOperation == null)
                    throw new InvalidOperationException("No foreground operation running");

                if (currentForegroundOperation != invokeRequest)
                    throw new InvalidOperationException("Current foreground request does not match the ending operation");

                currentForegroundOperation = null;

                if (ShowProgress(invokeRequest.Options))
                {
                    contentFrame.LoadingMessage = null;
                    contentFrame.IsContentEnabled = true;
                    contentFrame.ContentOpacity = 1;
                    if (contentFrame.ShowProgressBar)
                        contentFrame.ShowProgressBar = false;
                    if (contentFrame.ProgressBarVisibility == Visibility.Visible)
                        contentFrame.ProgressBarVisibility = Visibility.Collapsed;
                }

                OnForegroundOperationComplete(EventArgs.Empty);
            }
        }

        static bool IsForeground(InvokeOptions options)
        {
            return (options & InvokeOptions.InBackground) != InvokeOptions.InBackground;
        }

        static bool ShowProgress(InvokeOptions options)
        {
            return (options & InvokeOptions.NoProgress) != InvokeOptions.NoProgress;
        }

        public void RegisterChild(IContentFrame targetFrame)
        {
            if (CurrentRequest == null || targetFrame == contentFrame)
                return;

            if (!children.ContainsKey(CurrentRequest))
                children.Add(CurrentRequest, new List<IContentFrame>());
            
            if (!children[CurrentRequest].Contains(targetFrame))
            {
                children[CurrentRequest].Add(targetFrame);
                targetFrame.SetParent(contentFrame);
            }

            targetFrame.InitialiseHost(Host);
        }

        public void DeactivateCurrentRequest()
        {
            if (CurrentRequest == null || isDeactivating) return;

            isDeactivating = true;
            if (children.ContainsKey(CurrentRequest))
            {
                foreach (var child in children[CurrentRequest])
                {
                    child.DeactivateCurrentRequest();
                }
            }
            CurrentRequest.Deactivate();
            isDeactivating = false;
        }

        public void ActivateCurrentRequest()
        {
            if (CurrentRequest == null || isActivating) return;

            isActivating = true;
            if (children.ContainsKey(CurrentRequest))
            {
                foreach (var child in children[CurrentRequest])
                {
                    child.ActivateCurrentRequest();
                }
            }
            CurrentRequest.Activate();
            isActivating = false;
        }

        public void InitialiseHost(IPhoenixHost host)
        {
            Host = host;
        }

        public void CurrentRequestClosing(RequestClosingEventArgs eventArgs)
        {
            if (CurrentRequest == null || isClosing || eventArgs.Cancel) return;

            isClosing = true;
            if (children.ContainsKey(CurrentRequest))
            {
                foreach (var child in children[CurrentRequest])
                {
                    child.CurrentRequestClosing(eventArgs);
                    if (eventArgs.Cancel) return;
                }
            }
            CurrentRequest.Closing(eventArgs);
            isClosing = false;
        }

        public void InvalidateChildren()
        {
            if (CurrentRequest == null || isInvalidatingChildren) return;

            isInvalidatingChildren = true;
            if (children.ContainsKey(CurrentRequest))
            {
                foreach (var child in children[CurrentRequest])
                {
                    child.DeactivateCurrentRequest();
                }
                children[CurrentRequest].Clear();
            }
            isInvalidatingChildren = false;
        }

        public ChildFrameCollection ChildFrames
        {
            get { return childFrameCollection; }
        }

        public IContentFrame ParentFrame { get; private set; }

        public void SetParent(IContentFrame parentFrame)
        {
            ParentFrame = parentFrame;
        }

        public event NavigationCompleteEvent NavigationComplete;
        public event EventHandler ForegroundOperationStarted;
        public event EventHandler ForegroundOperationComplete;

        protected void OnForegroundOperationComplete(EventArgs e)
        {
            var handler = ForegroundOperationComplete;
            if (handler != null) handler(this, e);
        }

        protected void OnForegroundOperationStarted(EventArgs args)
        {
            var handler = ForegroundOperationStarted;
            if (handler != null) handler(this, args);
        }

        protected void OnNavigationComplete(NavigationCompleteEventArgs args)
        {
            var handler = NavigationComplete;
            if (handler != null) handler(this, args);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (isDisposing) return;
                isDisposing = true;

                foreach (var child in children.SelectMany(s => s.Value))
                {
                    child.Dispose();
                }
                children.Clear();
                if (CurrentRequest != null)
                {
                    CurrentRequest.Deactivate();
                    CurrentRequest = null;
                }
                isDisposing = false;
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        public IPhoenixHost Host { get; private set; }
    }
}